import {AxdrType} from "./AxdrType";
import {ReverseByteArrayInputStream} from "../ReverseByteArrayInputStream";
import {ReverseByteArrayOutputStream} from "../ReverseByteArrayOutputStream";
import {Buffer} from "buffer";
import {AxdrLength} from "./AxdrLength";


export abstract class AxdrSequenceOf<E extends AxdrType> implements AxdrType {


    dataCode: Buffer | null = null;
    length: number = -1;
    seqOf: Array<E> | null = null;

    constructor() {
        this.seqOf = new Array<E>();
    }

    set_dataCode(dataCode: Buffer) {
        this.dataCode = dataCode;
        this.seqOf = new Array<E>();
    }

    set_seq(seqof: Array<E>) {
        this.seqOf = seqof;
    }

    set_length(length: number) {
        this.length = length;
        this.seqOf = new Array<E>(length);
    }

    getDataCode(): Buffer {
        if (this.dataCode != null) {
            return this.dataCode;
        } else {
            throw new Error("dataCode is null");
        }
    }

    decode(input: ReverseByteArrayInputStream): number {
        let codeLength = 0;
        let numElements = this.length == -1 ? 0 : this.length;

        if (numElements == 0) {
            let l = new AxdrLength(0);
            codeLength += l.decode(input);

            numElements = l.getValue();
        }

        this.seqOf = new Array<E>();

        for (let i = 0; i < numElements; i++) {
            let subElem = this.createListElement();
            codeLength += subElem.decode(input);
            this.seqOf.push(subElem);
        }

        return codeLength;

    }

    encode(output: ReverseByteArrayOutputStream): number {
        let codeLength;

        if (this.dataCode != null) {
            codeLength = this.dataCode.length;
            for (let i = this.dataCode.length - 1; i >= 0; i--) {
                output.write(this.dataCode[i]);
            }
        } else {
            if (this.length != -1 && this.length != this.seqOf.length) {
                throw new Error("Error decoding AxdrSequenceOf: Size of elements does not match.");
            }

            codeLength = 0;
            for (let i = (this.seqOf.length - 1); i >= 0; i--) {
                codeLength += this.seqOf[i].encode(output);
            }

            if (this.length == -1) {
                codeLength += AxdrLength.encodeLength(output, this.seqOf.length);
            }

        }
        return codeLength;
    }

    encodeAndSave(encodingSizeGuess: number) {
        let revOStream = new ReverseByteArrayOutputStream();
        revOStream.setBufSize(encodingSizeGuess)
        this.encode(revOStream);
        this.dataCode = revOStream.getArray();
    }

    add(element : E){
        if (this.length != 0 && this.seqOf.length == this.length) {
            console.log(this.length + " " + this.seqOf.length)
            throw new Error("array index out of bound");
        }
        this.seqOf.push(element);
    }

    get(index : number) : E{
        return this.seqOf[index];
    }

    size() : number{
        return this.seqOf.length;
    }

    list() : Array<E>{
        return this.seqOf;
    }

    abstract createListElement(): E;

}

